package slicelearn

import "fmt"

func DeclareSlice() {
	//什么是切片
	//数组有一个很大的问题：大小确定，不能修改。切片可以理解为动态的数组

	//1.切片定义
	//var courses []string //定义了一个切片

	//初始化，切片值
	courses1 := []string{"django", "scrapy", "tornado"}
	fmt.Printf("%T\n", courses1)

	//2.切片的另一种初始化方法， make
	//切片不是某有长度限制吗？为什么使用make初始化切片需要传递一个长度，那么传了长度后，切片的是不是意味着和数组一样不能变了？
	courses2 := make([]string, 5)
	fmt.Printf("%T, len(courses2)=%d, cap(courses2)=%d \n", courses2, len(courses2), cap(courses2))
	//slice对表的是python的list,但底层是不一样的

	//3.第三种方法，通过数组变成一个却骗
	var courses3 = [5]string{"django", "scrapy", "tornado", "python", "asyncio"}

	//python中的切片是一种用法，go中的切片是一种数据类型
	subCourses3 := courses3[1:4]
	fmt.Printf("%T, len(subCourses3)=%d, cap(subCourses3)=%d \n", subCourses3, len(subCourses3), cap(subCourses3))

	//对切片修改，影响原来的数组吗？
	fmt.Println("courses3=", courses3)
	fmt.Println("subCourses3=", subCourses3)
	subCourses3[0] = "fastapi"
	fmt.Println("courses3=", courses3)

	//4.第四种方法，new
	courses4 := *new([]string)
	fmt.Printf("%T", courses4)
}

func StrangePhenomenon() {
	//python和go语言的slice区别：go的slice更像是python的list,go语言slice是基于数组实现的，python的list也是基于数据（C语言数组）
	//slice上进行的操作会影响原来的数组，slice更像是一个指针，本身不存值
	//python的列表的切片，是一个复制，不会影响原来的list

	//1.第一个现象
	a := make([]int, 0)
	b := []int{1, 2, 3}
	fmt.Println(copy(a, b))
	fmt.Println(a) //为空，原因是不会主动扩展切片

	//2.第二个现象
	c := b[:]
	c[0] = 8
	fmt.Println("b=", b) //影响到原切片，原因是：内部指向同一个内存空间
	fmt.Println("c=", c)
	fmt.Println("len(b)=", len(b))
	fmt.Println("cap(b)=", cap(b))
	fmt.Println("len(c)=", len(c))
	fmt.Println("cap(c)=", cap(c))

	//3.第三个现象
	c = append(c, 9, 8)
	fmt.Println("b=", b) //append函数没有影响到原来的切片
	fmt.Println("c=", c)
	c[0] = 11
	fmt.Println("b=", b) //append再修改就不再影响原切片了？
	//这是因为产生了扩容，扩容机制一旦触发，这个时候切片就会指向新的内存地址
	//如果没有扩容，就还会影响原来的切片
	fmt.Println("c=", c)

	//4.第四个现象
	fmt.Println("len(b)=", len(b))
	fmt.Println("cap(b)=", cap(b))
	fmt.Println("len(c)=", len(c))
	fmt.Println("cap(c)=", cap(c)) //cap容量，长度和容量该怎么理解

	//切片扩容会影响速度
	oldSlice := make([]int, 0)
	fmt.Printf("len(oldSlice)=%d, cap(oldSlice)=%d\n", len(oldSlice), cap(oldSlice))

	oldSlice = append(oldSlice, 1)
	fmt.Printf("第一次append后，len(newSlice)=%d, cap(newSlice)=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 2)
	fmt.Printf("第二次append后，len(newSlice)=%d, cap(newSlice)=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 3)
	fmt.Printf("第三次append后，len(newSlice)=%d, cap(newSlice)=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 4)
	fmt.Printf("第四次append后，len(newSlice)=%d, cap(newSlice)=%d\n", len(oldSlice), cap(oldSlice))
	oldSlice = append(oldSlice, 5)
	fmt.Printf("第五次append后，len(newSlice)=%d, cap(newSlice)=%d\n", len(oldSlice), cap(oldSlice))
	/*
		Go 中切片扩容的策略是这样的：

		首先判断，如果新申请容量（cap）大于2倍的旧容量（old.cap），最终容量（newcap）就是新申请的容量（cap）
		否则判断，如果旧切片的长度小于1024，则最终容量(newcap)就是旧容量(old.cap)的两倍，即（newcap=doublecap）
		否则判断，如果旧切片长度大于等于1024，则最终容量（newcap）从旧容量（old.cap）开始循环增加原来的 1/4，即（newcap=old.cap,for {newcap += newcap/4}）直到最终容量（newcap）大于等于新申请的容量(cap)，即（newcap >= cap）
		如果最终容量（cap）计算值溢出，则最终容量（cap）就是新申请容量（cap）
	*/
	//如果小于1024 扩容的速度是2倍 如果大于了1024 扩容的速度就是1.25

	//切片来说 1. 底层是数组，如果是基于数组产生的 会有一个问题就是会影响原来的数组。
	//2. 切片的扩容机制
	//3. 切片的传递是引用传递
}

func IntroSliceLenCap() {
	//1.make方式创建slice
	d := make([]int, 5, 6)
	fmt.Printf("make方式创建slice: %v,  type(d)=%T, len(d)=%d, cap(d)=%d \n", d, d, len(d), cap(d))

	//2.通过数组取切片
	data := [...]int{1: 2, 3: 7, 7: 10, 9: 100}
	ds := data[1:4]
	fmt.Printf("对数组进行切片: %v,  type(ds)=%T, len(ds)=%d, cap(ds)=%d \n", ds, ds, len(ds), cap(ds))

	//3.直接声明
	slice := []int{1, 2, 3}
	fmt.Printf("直接声明: %v,  type(slice)=%T, len(slice)=%d, cap(slice)=%d \n", slice, slice, len(slice), cap(slice))

}

func printSlice(toPrint []string) {
	toPrint[0] = "test0"
	fmt.Println("在函数体内部改变切片的值：", toPrint)
}

func SliceIsReferenceType() {
	//数组的传递是值传递，切片的传递是引用传递
	var courses1 = []string{"python1", "golang1", "django1"}
	fmt.Println("切片初始值：", courses1)
	printSlice(courses1)
	fmt.Println("查看原切片：", courses1)
}

func SliceDefaultValue() {
	//go语言中slice的原理讲解很重要，这个有坑（对于初学者），有经验的程序员觉得这个不是坑
	//程序员也是消费者-java c++ go -静态语言是站在对处理器的角度考虑， python - 才会站在使用者的角度上去考虑，对于处理器就不友好
	//当make遇到了append容易出现的坑
	s1 := make([]int, 5)
	s1 = append(s1, 6)
	//对于很多初学者来说我们期望的是只有一个数字就是6，其实是[0,0,0,0,0,6]
	fmt.Println(s1)
	//很多人对make函数产生了一个假象 ，s1 := make([]int, 5) 好比是python的 s1 = []
}
